#include "limage.h"

LImage::LImage()
{
    histogram=NULL;
}
LImage::LImage(QImage qImage)
{
    image = qImage;
    histogram=NULL;
}

LImage::LImage(int width,int height,QImage::Format format):image(width,height,format)
{
    histogram=NULL;
}

LImage::LImage(QString fileName):image(fileName)
{
    histogram=NULL;
}

QImage LImage::getImage()
{
    return image;
}

void LImage::loadLimage(QString path)
{
     this->image.load(path);
}

QRgb LImage::pixel(int i,int j)
{
    return (this->image.pixel(i,j));
}

void LImage::setPixel(int i,int j,int red,int green,int blue)
{
    if(red > 255)
        red = 255;
    if(green > 255)
        green = 255;
    if(blue > 255)
        blue = 255;
    if(red < 0)
        red = 0;
    if(green < 0)
        green = 0;
    if(blue < 0)
        blue = 0;

    QColor color= QColor ::fromRgb(red,green,blue,255);
    QRgb formedRgb=color.rgb();
    this->image.setPixel(i,j,formedRgb);
}
void LImage::setPixel(int i,int j,double red,double green,double blue)
{

    int redInt,blueInt,greenInt;
    redInt = floor(red);
    blueInt= floor(blue);
    greenInt=floor(green);

    if(redInt > 255)
        redInt = 255;
    if(greenInt > 255)
        greenInt = 255;
    if(blueInt > 255)
        blueInt = 255;
    if(redInt < 0)
        redInt = 0;
    if(greenInt < 0)
        greenInt = 0;
    if(blueInt < 0)
        blueInt = 0;
    QColor color= QColor ::fromRgb(redInt,greenInt,blueInt,255);
    QRgb formedRgb=color.rgb();
    this->image.setPixel(i,j,formedRgb);
}


LImage LImage ::convertToGrayScale()
{
    double resultValue;
    LImage returnedImage(this->image.width(),this->image.height(),this->image.format());
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
        {
            resultValue= 0.3*qRed(image.pixel(i,j))+0.59*qGreen(image.pixel(i,j))+0.11*qBlue(image.pixel(i,j));
            returnedImage.setPixel(i,j,resultValue,resultValue,resultValue);
        }
    return returnedImage;
}

//CONTINUE HERE
LImage LImage ::convertToNegative()
{
    LImage returnedImage(image.width(),image.height(),image.format());
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
        {
            int red=255-qRed(image.pixel(i,j));
            int green=255-qGreen(image.pixel(i,j));
            int blue=255-qBlue(image.pixel(i,j));
            returnedImage.setPixel(i,j,red,green,blue);
        }
     return returnedImage ;
}

LImage LImage::normalizeImage()
{
    LImage returnedImage(image.width(),image.height(),image.format());
    int max=qRed(image.pixel(0,0));;
    int min=qRed(image.pixel(0,0));
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
        {
          int r=qRed(image.pixel(i,j));
          int g=qGreen(image.pixel(i,j));
          int b=qBlue(image.pixel(i,j));
           if(max<r)
               max=r;
           if(max<g)
               max=g;
           if(max<b)
               max=b;
           if(min>r)
               min=r;
           if(min>g)
               min=g;
           if(min>b)
               min=b;
        }

    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
        {
            int r=qRed(image.pixel(i,j));
            int g=qGreen(image.pixel(i,j));
            int b=qBlue(image.pixel(i,j));
            r=(r-min)*255.0/(max-min);
            g=(g-min)*255.0/(max-min);
            b=(b-min)*255.0/(max-min);
            returnedImage.setPixel(i,j,r,g,b);


        }
    return returnedImage;
}


LImage LImage:: mergeGrayImage(double factor1, LImage secondImage,double  factor2)
{
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());

    QRgb firstRgb,secondRgb;
    QColor firstColor,secondColor;

    if((this->image.width()!=secondImage.image.width())||this->image.height()!=secondImage.image.height())
    {
        //cout<<"The Two Images Cannot Be Merged Because They Do not Have the Same Dimenesions"<<endl;
        return secondImage;
    }

    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            double firstComponent,secondComponent,resultComponent;
            firstRgb= this->pixel(i,j);
            firstColor=QColor::fromRgb(firstRgb);
            firstComponent= firstColor.red();

            secondRgb= secondImage.image.pixel(i,j);
            secondColor=QColor::fromRgb(secondRgb);
            secondComponent= secondColor.red();
            resultComponent=factor1*firstComponent+factor2*secondComponent;
            outputImage.setPixel(i,j,resultComponent,resultComponent,resultComponent);


        }
        }
    return outputImage;


}
LImage LImage::mergeImages(LImage other, double coef, int x, int y)
{
    LImage output(image.width(),image.height(),image.format());
    QImage o=other.getImage();
    if(coef<0 || coef>1)
        return *this;
    double mycoef=1.0-coef;
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
        {
            int ri=qRed(image.pixel(i,j));
            int gi=qGreen(image.pixel(i,j));
            int bi=qBlue(image.pixel(i,j));

            if(i<x || i>=o.width()+x || j<y || j>=o.height()+y)
                output.setPixel(i,j,ri,gi,bi);
            else
            {
                int io=i-x;
                int jo=j-y;
                int ro=qRed(o.pixel(io,jo));
                int go=qGreen(o.pixel(io,jo));
                int bo=qBlue(o.pixel(io,jo));
                ro=ri*mycoef+ro*coef;
                go=gi*mycoef+go*coef;
                bo=bi*mycoef+bo*coef;
                output.setPixel(i,j,ro,go,bo);
            }
        }
    return output;
}

LImage LImage:: mergeColorImage(double factor1, LImage secondImage,double  factor2)
{
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    QRgb firstRgb,secondRgb;
    QColor firstColor,secondColor;

    if((this->image.width()!=secondImage.image.width())||this->image.height()!=secondImage.image.height())
    {
        //cout<<"The Two Images Cannot Be Merged Because They Do not Have the Same Dimenesions"<<endl;
        return secondImage;
    }

    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            double firstRed,secondRed,resultRed;
            double firstBlue,secondBlue,resultBlue;
            double firstGreen,secondGreen,resultGreen;
            firstRgb= this->pixel(i,j);
            firstColor=QColor::fromRgb(firstRgb);
            firstRed= firstColor.red();
            firstGreen= firstColor.green();
            firstBlue= firstColor.blue();


            secondRgb= secondImage.image.pixel(i,j);
            secondColor=QColor::fromRgb(secondRgb);
            secondRed= secondColor.red();
            secondGreen= secondColor.green();
            secondBlue= secondColor.blue();

            resultRed=factor1*firstRed+factor2*secondRed;
            resultGreen=factor1*firstGreen+factor2*secondGreen;
            resultBlue=factor1*firstBlue+factor2*secondBlue;
            outputImage.setPixel(i,j,resultRed,resultGreen,resultBlue);
        }
        }
    return outputImage;


}
LImage LImage::mergeImage(double factor1,LImage secondImage,double factor2)
{
    LImage outputImage;
    if((this->image.isGrayscale()==true)&&(secondImage.image.isGrayscale()==true))
    {
        outputImage =this->mergeGrayImage(factor1,secondImage,factor2);
        return outputImage;
    }
    else
        if((this->image.isGrayscale()==false)&&(secondImage.image.isGrayscale()==true))
        {

            //this= this->convertToGrayScale();
           //return  this->mergeGrayImage(factor1,secondImage,factor2);
        return this->mergeColorImage(factor1,secondImage,factor2);

        }
    else
        if((this->image.isGrayscale()==true)&&(secondImage.image.isGrayscale()==false))
        {
            //secondImage=secondImage.convertToGrayScale();
            //return this->mergeGrayImage(factor1,secondImage,factor2);
            return this->mergeColorImage(factor1,secondImage,factor2);
        }
    else
        //cout<<"Heey I am There";
        return mergeColorImage(factor1,secondImage,factor2);

}
void LImage ::saveImage(QString filePath)
{
    this->image.save(filePath,NULL);
}

LImage LImage ::grayHistogramEqualization()
{
    unsigned long* CDF;
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    calcHistogram();
    this->histogram->calcCDF();
    HistogramGray* histogramGray=(HistogramGray*)(histogram);
     CDF= histogramGray->getCDF();
            double max =CDF[255];

    double factor = 255/max;
   QColor retrivedColor;
   QRgb retrivedRgb;
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            double formedComponent;

            retrivedRgb= this->pixel(i,j);
            retrivedColor= QColor::fromRgb(retrivedRgb);
            formedComponent= CDF[retrivedColor.red()]*factor ;
            outputImage.setPixel(i,j,formedComponent,formedComponent,formedComponent);


        }
        }
    delete histogram;
    return outputImage;

}

LImage LImage::RGBHistogramEqualization()
{
    unsigned long* redCDF;
    unsigned long* greenCDF;
    unsigned long* blueCDF;
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    calcHistogram();
    this->histogram->calcCDF();
    HistogramRGB* RGBhisto= (HistogramRGB*) (this->histogram);
    redCDF=RGBhisto->getRedCDF();
    double maxRed =redCDF[255];
    double redFactor = 255/maxRed;
    greenCDF= RGBhisto->getGreenCDF();
    blueCDF=RGBhisto->getBlueCDF();
    double maxBlue,maxGreen;
    double greenFactor,blueFactor;
    maxGreen= greenCDF[255];
    maxBlue=blueCDF[255];
    greenFactor= 255/maxGreen;
    blueFactor= 255/maxBlue;
   QColor retrivedColor;
   QRgb retrivedRgb;
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            double formedRed,formedGreen,formedBlue;

            retrivedRgb= this->pixel(i,j);
            retrivedColor= QColor::fromRgb(retrivedRgb);
           // formedComponent= CDF[retrivedColor.red()]*factor ;

            formedRed= redCDF[retrivedColor.red()]*redFactor;
            formedGreen= greenCDF[retrivedColor.green()]*greenFactor;
            formedBlue= blueCDF[retrivedColor.blue()]*blueFactor;
            outputImage.setPixel(i,j,formedRed,formedGreen,formedBlue);


        }
        }
    delete histogram;
    return outputImage;

}

/*LImage LImage ::HSIHistogramEqualization()
{
    QRgb retrivedRGB;
    QColor retrivedColor,HSIColor;
    double

    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            double formedRed,formedGreen,formedBlue;

            retrivedRgb= this->pixel(i,j);

            retrivedColor= QColor::fromRgb(retrivedRgb);
           HSIColor= retrivedColor.toHsl();
           // formedComponent= CDF[retrivedColor.red()]*factor ;
            if(retrivedColor.blue()<0)
            {
                cout<<"hi";
            }
            formedRed= redCDF[retrivedColor.red()]*redFactor;
            formedGreen= greenCDF[retrivedColor.green()]*greenFactor;
            formedBlue= blueCDF[retrivedColor.blue()]*blueFactor;
            outputImage.setPixel(i,j,formedRed,formedGreen,formedBlue);


        }
        }
}*/
LImage LImage::HSIHistogramEqualization()
{
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    unsigned long * intensityCDF;
    double maxIntensity;
    this->histogram = new HistogramHSI(&this->image);
    this->histogram->calcHistogram();
    this->histogram->calcCDF();
    HistogramHSI* historgram= (HistogramHSI*)this->histogram;
    intensityCDF= historgram->getIntensityCDF();
    maxIntensity = intensityCDF[255];
    double factor= 255/maxIntensity;
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
            QRgb tempRgb = this->pixel(i,j);
            QColor tempColor= QColor::fromRgb(tempRgb);
            double formedIntensity;
            QColor HSIColor = tempColor.toHsl();
            formedIntensity= intensityCDF[HSIColor.value()]*factor;
            int intIntisity = floor(formedIntensity);
            QColor formedColor= QColor ::fromHsv(HSIColor.hsvHue(),HSIColor.hsvSaturation(),intIntisity,255);


            outputImage.setPixel(i,j,formedColor.red(),formedColor.green(),formedColor.blue());


        }
    }
    delete histogram;
    return outputImage;
}

LImage LImage::histogramEqualization()
{
    if(this->image.isGrayscale())
    {
        return this->grayHistogramEqualization();
    }
    else
    {
        return this->RGBHistogramEqualization();
    }
}
LImage LImage ::histogramStretching()
{
    if(image.isGrayscale())
        return grayHistogramStretching();
    else
        return colorHSIHistogramStretching();
}

LImage LImage ::grayHistogramStretching()
{
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    int max,min,tempComponent;
    double doubleTempComponent;
    QColor tempColor;
    max =0;
    min= 256;
    QRgb tempRGB;
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
          tempRGB = this->pixel(i,j);
          tempColor= QColor::fromRgb(tempRGB);
          tempComponent= tempColor.red();
          if(tempComponent>max)
          {
              max=tempComponent;
          }
          if(tempComponent<min)
          {
              min= tempComponent;
          }
        }

    }
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
          tempRGB = this->pixel(i,j);
          tempColor= QColor::fromRgb(tempRGB);
          tempComponent= tempColor.red();
          double factor= 255/(double)(max-min);
          doubleTempComponent= tempComponent;
          doubleTempComponent= (doubleTempComponent-min)*factor;
          outputImage.setPixel(i,j,doubleTempComponent,doubleTempComponent,doubleTempComponent);
        }

    }
    return outputImage;


}
LImage LImage ::colorHSIHistogramStretching()
{
    LImage outputImage(this->image.width(),this->image.height(),this->image.format());
    int max,min,tempValue;
    double doubleTempValue;
    QColor tempColor;
    max =0;
    min= 256;
    QRgb tempRGB;
    int formedValue;
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
          tempRGB = this->pixel(i,j);
          tempColor= QColor::fromRgb(tempRGB);
          tempValue= tempColor.value();
          if(tempValue>max)
          {
              max=tempValue;
          }
          if(tempValue<min)
          {
              min= tempValue;
          }
        }

    }
    for(int i=0;i<this->image.width();i++)
    {
        for(int j=0;j<this->image.height();j++)
        {
          tempRGB = this->pixel(i,j);
          tempColor= QColor::fromRgb(tempRGB);
          tempValue= tempColor.value();
          double factor= 255/(double)(max-min);
          doubleTempValue= tempValue;
          doubleTempValue = (doubleTempValue-min)*factor;
          formedValue= (int)doubleTempValue;
          QColor formedColor = QColor ::fromHsv(tempColor.hsvHue(),tempColor.hsvSaturation(),formedValue);
          outputImage.setPixel(i,j,formedColor.red(),formedColor.green(),formedColor.blue());
        }

    }
    return outputImage;


}
LImage LImage::intelligentScissors()
{
    return *this;
}

QPixmap LImage::getPixmap()
{
    return QPixmap::fromImage(image);
}

void LImage::calcHistogram()
{
    if(image.isNull())
        histogram=NULL;
    else if(image.isGrayscale())
        histogram = new HistogramGray(&image);
    else
        histogram= new HistogramRGB(&image) ;
    if(histogram!=NULL)
    histogram->calcHistogram();
}
QImage LImage::getHistogramImage()
{
    calcHistogram();
    QImage im=histogram->getHistImage();
    delete histogram;
    return im;
}

QImage LImage::getHistogramYUVImage()
{
    histogram=new HistogramYUV(&image);
    QImage im=histogram->getHistImage();
    delete histogram;
    return im;
}

LImage LImage::crop(int startx, int starty, int endx, int endy)
{
    int width=image.width();
    int height=image.height();
    if(endx>=width)
        endx=width-1;
    if(startx < 0)
        startx=0;
    if(endy >= height)
        endy=height;
    if(starty < 0)
        starty=0;
    int newwidth=endx-startx;
    int newheight=endy-starty;
    QImage newmi(newwidth,newheight,image.format());
    for(int newy=0;newy<newheight;newy++)
        for(int newx=0;newx<newwidth;newx++)
        {
            int x=newx+startx;
            int y=newy+starty;
            QRgb value=image.pixel(x,y);
            newmi.setPixel(newx,newy,value);
        }
    return LImage(newmi);
}

LImage LImage::resizeAverage(int newwidth, int newheight)
{

    if(newwidth < 1) newwidth=1;
    if(newheight < 1) newheight=1;
    QImage newmi(newwidth,newheight,image.format());
    int width=image.width();
    int height=image.height();

    float scaley=((float)height-1)/(newheight-1);
    float scalex=((float)width-1)/(newwidth-1);

    for(int newy=0;newy<newheight;newy++)
        for(int newx=0;newx<newwidth;newx++)
        {
            float x=newx*scalex;
            float y=newy*scaley;
            float xn= x+1<width? x+1:x; //x+1
            float yn= y+1<height? y+1:y; //y+1
            int ne=image.pixel(x,y);
            int no=image.pixel(xn,y);
            int se=image.pixel(x,yn);
            int so=image.pixel(xn,yn);

            //Average
            int red=round(qRed(ne)*.25f+qRed(no)*.25f+qRed(se)*.25f+qRed(so)*.25f);
            int green=round(qGreen(ne)*.25f+qGreen(no)*.25f+qGreen(se)*.25f+qGreen(so)*.25f);
            int blue=round(qBlue(ne)*.25f+qBlue(no)*.25f+qBlue(se)*.25f+qBlue(so)*.25f);
            newmi.setPixel(newx,newy,qRgb(red,green,blue));
        }
    return LImage(newmi);
}

LImage LImage::resizeBilinear(int newwidth, int newheight)
{
    if(newwidth < 1) newwidth=1;
    if(newheight < 1) newheight=1;
    QImage newmi(newwidth,newheight,image.format());
    int width=image.width();
    int height=image.height();
    //cout << "Using Average Resizing" << endl;
    //cout << "Converting image with width= " << width << ", height= " << height << endl;
    //cout << "To new image with width= " << newwidth << ", height= " << newheight << endl;

    float scaley=((float)height-1)/(newheight-1);
    float scalex=((float)width-1)/(newwidth-1);

    for(int newy=0;newy<newheight;newy++)
        for(int newx=0;newx<newwidth;newx++)
        {
            float x=newx*scalex;
            float y=newy*scaley;
            float xn= x+1<width? x+1:x; //x+1
            float yn= y+1<height? y+1:y; //y+1
            float dx=x-floor(x);
            float dy=y-floor(y);
            int ne=image.pixel(x,y);
            int no=image.pixel(xn,y);
            int se=image.pixel(x,yn);
            int so=image.pixel(xn,yn);

            //Weights
            float wne=dx*dy;
            float wno=(1-dx)*dy;
            float wse=dx*(1-dy);
            float wso=(1-dx)*(1-dy);

            //Calculate RGB Values
            int red=round(qRed(ne)*wne+qRed(no)*wno+qRed(se)*wse+qRed(so)*wso);
            int green=round(qGreen(ne)*wne+qGreen(no)*wno+qGreen(se)*wse+qGreen(so)*wso);
            int blue=round(qBlue(ne)*wne+qBlue(no)*wno+qBlue(se)*wse+qBlue(so)*wso);
            newmi.setPixel(newx,newy,qRgb(red,green,blue));
        }

    return LImage(newmi);
}

QImage LImage::rotate90clock(QImage image)
{
    QImage newim(image.height(),image.width(),image.format());
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
            newim.setPixel(image.height()-1-j,i,image.pixel(i,j));
    return newim;
}

QImage LImage::rotateclock(int angle)
{
    angle=angle%360;
    QImage newim=image;
    for(;angle>0;angle-=90)
        newim=rotate90clock(newim);
    return newim;
}

QImage LImage::rotate90counterclock(QImage image)
{
    QImage newim(image.height(),image.width(),image.format());
    for(int i=0;i<image.width();i++)
        for(int j=0;j<image.height();j++)
            newim.setPixel(j,image.width()-1-i,image.pixel(i,j));
    return newim;
}

QImage LImage::rotatecounterclock(int angle)
{
        angle=angle%360;
        QImage newim=image;
        for(;angle>0;angle-=90)
            newim=rotate90counterclock(newim);
        return newim;
}


LImage::~LImage()
{
}




